package com.lucien.framework.websocket.interceptor;

import com.lucien.common.utils.StringUtils;
import com.lucien.framework.websocket.UserPrincipal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 消息通道拦截器
 */
@Component
public class WebSocketChannelInterceptor implements ChannelInterceptor {

    private static final Logger log = LoggerFactory.getLogger(WebSocketChannelInterceptor.class);

    /**
     * 存放所有在线的客户端
     */
    private static Map<String, String> clients = new ConcurrentHashMap<String, String>();

    /**
     * 在消息发送之前调用，方法中可以对消息进行修改，
     * 如果此方法返回值为空，则不会发生实际的消息发送调用
     *
     * @param message
     * @param channel
     * @return
     */
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

        // 判断是否首次连接
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            //获取 stompClient.connect中header的信息
            String token = accessor.getNativeHeader("Authorization").get(0);

            /*
             * 1. 这里直接封装到StompHeaderAccessor 中，可以根据自身业务进行改变
             * 2. 封装大搜StompHeaderAccessor中后，可以在@Controller / @MessageMapping注解的方法中直接带上StompHeaderAccessor
             *    就可以通过方法提供的 getUser()方法获取到这里封装user对象
             * 2. 例如可以在这里拿到前端的信息进行登录鉴权
             */
            UserPrincipal user = (UserPrincipal) accessor.getUser();
            log.info("认证用户:" + user.getName());
        } else if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            log.info("用户:" + accessor.getUser() + " 断开连接");
        }
        return message;
    }

    /**
     * 在消息发送后立刻调用
     *
     * @param message
     * @param channel
     * @param sent    表示该调用的返回值
     */
    @Override
    public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        /*
         * 拿到消息头对象后，我们可以做一系列业务操作
         * 1. 通过getSessionAttributes()方法获取到websocketSession，
         *    就可以取到我们在WebSocketHandshakeInterceptor拦截器中存在session中的信息
         * 2. 我们也可以获取到当前连接的状态，做一些统计，例如统计在线人数，或者缓存在线人数对应的令牌，方便后续业务调用
         */

        Map<String, Object> attributes = accessor.getSessionAttributes();

        String session = null;
        for (Map.Entry<String, Object> entry : attributes.entrySet()) {
            if (StringUtils.equals(entry.getKey(), "HTTP.SESSION.ID")) {
                session = String.valueOf(entry.getValue());
            }
        }
        if (StringUtils.isNull(accessor.getCommand())) {
            return;
        }
        switch (accessor.getCommand()) {
            // 首次连接
            case CONNECT:
                log.info("httpSession key：" + session + " 首次连接");
                Object headers = message.getHeaders().get("nativeHeaders");
                break;
            // 连接中
            case CONNECTED:
                break;
            case SUBSCRIBE:
                break;
            // 下线
            case DISCONNECT:
                log.info("httpSession key：" + session + " 下线");
                break;
            default:
                break;
        }

    }

    /**
     * 使用正则表达式提取中括号中的内容
     * @param msg
     * @return
     */
    public static List<String> extractMessageByRegular(String msg){
        List<String> list=new ArrayList<String>();
        Pattern p = Pattern.compile("(\\[[^\\]]*\\])");
        Matcher m = p.matcher(msg);
        while(m.find()){
            list.add(m.group().substring(1, m.group().length()-1));
        }
        return list;
    }

    /*
     * 1. 在消息发送完成后调用，而不管消息发送是否产生异常，在次方法中，我们可以做一些资源释放清理的工作
     * 2. 此方法的触发必须是preSend方法执行成功，且返回值不为null,发生了实际的消息推送，才会触发
     */
    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel messageChannel, boolean b, Exception e) {
//        log.info("preSend方法执行成功");
        MessageHeaders headers = message.getHeaders();
        String simpMessageType = headers.get("simpMessageType").toString();
        switch (simpMessageType) {
            case "CONNECT":
                log.info("CONNECT：" + headers.get("nativeHeaders").toString());
                break;
            case "SUBSCRIBE":
                log.info("订阅地址" + headers.get("nativeHeaders").toString());
                break;
            case "MESSAGE":
                log.info("发送消息地址" + headers.get("simpSessionAttributes").toString());
                break;
            default:
                break;
        }
    }
}
